#!/usr/bin/env python
# -*- coding: utf-8 -*-

#    This file is part of My Text Editor.
#
#    My Text Editor is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    My Text Editor is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with My Text Editor.  If not, see <http://www.gnu.org/licenses/>.

# Copyright (C) 2013 Joseba García Etxebarria <joseba.gar@gmail.com>. All rights reserved.

import os,sys
from PyQt4 import QtGui, QtCore
from PyQt4.Qt import QFrame, QWidget, QTextEdit, QHBoxLayout, QPainter, QColor
from syntaxhighlighter import highlighter
import cardreader
 
class LineTextWidget(QFrame):
    class TextBox(QTextEdit):
        def __init__(self, *args):
            QTextEdit.__init__(self, *args)

        def highlightCurrentLine(self):

            extraSelections = []

            if not self.isReadOnly():
                selection = QTextEdit.ExtraSelection()

                lineColor = QColor(41, 41, 41)

                selection.format.setBackground(lineColor)
                selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, True)
                selection.cursor = self.textCursor()
                selection.cursor.clearSelection()
                extraSelections.append(selection)

            self.setExtraSelections(extraSelections)

        def paintEvent(self, event):
            # Highlight the current line
            self.highlightCurrentLine()
            
            # Now, paint the Nastran field separator
            width  = QtGui.QFontMetrics(self.currentFont()).width('$.......2.......')/2.
            height = self.viewport().height()
            painter = QPainter(self.viewport())
            painter.setPen(QtGui.QColor(71, 69, 69))
            for i in range(10):
                painter.drawLine(width*(i+1)+4, 0, width*(i+1)+4, height)
            painter.end()
            
            QTextEdit.paintEvent(self, event)

        def keyPressEvent(self, event):
            #print ('Key: %s\n' % event.key())
            keyhandled = True
            cursor = self.textCursor()
            line = cursor.block().text()
            line = str(line)
            blocks, notation = cardreader.getFields(line)
            if event.key() == 16777217:
                # Tab
                if not event.modifiers():
                    pos = cursor.positionInBlock()
                    if notation == 'free':
                        if line.count(',') > 8:
                            cursor.insertText('\n,')
                        else:
                            cursor.insertText(',')
                    elif notation == 'short':
                        newpos = ( int(pos / 8) + 1 ) * 8
                        if newpos > 80:
                            cursor.insertText('\n        ')
                        else:
                            cursor.insertText(' '* (newpos-pos))
                    elif notation == 'long':
                        if pos > 7:
                            newpos = ( int( (pos) / 16) + 1 ) * 16 + 8
                        else:
                            newpos = 8
                        if newpos > 80:
                            cursor.insertText('\n*       ')
                        else:
                            cursor.insertText(' '* (newpos-pos))
                    else:
                        cursor.insertText(' '*4)
                    
                elif event.modifiers() & QtCore.Qt.ControlModifier:
                    cur = self.main_dialog.ui.tabWidget.currentIndex()
                    max = self.main_dialog.ui.tabWidget.count()
                    i   = cur + 1
                    if i >= max:
                        i = 0
                    self.main_dialog.ui.tabWidget.setCurrentIndex(i)
            elif event.key() == 16777264:
                # Open help
                self.main_dialog.open_nastranlibrary()
            elif event.key() == 16777220 and (event.modifiers() & QtCore.Qt.ControlModifier):
                # Control + Enter
                if line.lower().startswith('include'):
                    include = line.replace('"', '').replace("'", '').split()[1].replace('\\', '/')
                    include = os.path.join(self.dirname, include)
                    if os.path.isfile(include):
                        self.main_dialog.open_file(include)
            elif (event.key() == 43 and (event.modifiers() & QtCore.Qt.ControlModifier)) or event.key() == 93:
                self.zoomIn(2)
            elif event.key() == 45 and (event.modifiers() & QtCore.Qt.ControlModifier):
                self.zoomOut(2)
            elif event.key() == 49 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(0)
            elif event.key() == 50 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(1)
            elif event.key() == 51 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(2)
            elif event.key() == 52 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(3)
            elif event.key() == 53 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(4)
            elif event.key() == 54 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(5)
            elif event.key() == 55 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(6)
            elif event.key() == 56 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(7)
            elif event.key() == 57 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(8)
            elif event.key() == 58 and (event.modifiers() & QtCore.Qt.AltModifier):
                self.main_dialog.ui.tabWidget.setCurrentIndex(9)
            elif event.key() == 36 and (event.modifiers() & QtCore.Qt.ControlModifier):
                # Ctrl+Shitf+4
                cursor.movePosition(QtGui.QTextCursor.StartOfLine)
                cursor.insertText('$.......2.......3.......4.......5.......6.......7.......8.......9.......10......\n')
            else:
                keyhandled = False

            if not keyhandled:
                QTextEdit.keyPressEvent(self, event)

        def mousePressEvent(self, event):
            #print 'Mouse clicked\n'
            if (event.button() == QtCore.Qt.LeftButton) and (event.modifiers() & QtCore.Qt.ControlModifier):
                # Block selection
                pass
            
            QTextEdit.mousePressEvent(self, event)

        def setDocumentPath(self, filename):
            self.path     = os.path.normpath(str(filename))
            self.dirname  = os.path.dirname(self.path)
            self.filename = os.path.basename(self.path)
            
    class NumberBar(QWidget):
        def __init__(self, *args):
            QWidget.__init__(self, *args)
            self.edit = None
            # This is used to update the width of the control.
            # It is the highest line that is currently visibile.
            self.highest_line = 0
 
        def setTextEdit(self, edit):
            self.edit = edit
 
        def update(self, *args):
            '''
            Updates the number bar to display the current set of numbers.
            Also, adjusts the width of the number bar if necessary.
            '''
            # The + 4 is used to compensate for the current line being bold.
            width = self.fontMetrics().width(str(self.highest_line)) + 4
            if self.width() != width:
                self.setFixedWidth(width)
            QWidget.update(self, *args)

        def paintEvent(self, event):
            # First of all, paint all the line numbers, with the current one in bold
            contents_y = self.edit.verticalScrollBar().value()
            page_bottom = contents_y + self.edit.viewport().height()
            font_metrics = self.fontMetrics()
            current_block = self.edit.document().findBlock(self.edit.textCursor().position())
 
            painter = QPainter(self)
 
            line_count = 0
            # Iterate over all text blocks in the document.
            block = self.edit.document().begin()
            while block.isValid():
                line_count += 1
 
                # The top left position of the block in the document
                position = self.edit.document().documentLayout().blockBoundingRect(block).topLeft()
 
                # Check if the position of the block is out side of the visible
                # area.
                if position.y() > page_bottom:
                    break
 
                # We want the line number for the selected line to be bold.
                bold = False
                if block == current_block:
                    bold = True
                    font = painter.font()
                    font.setBold(True)
                    painter.setFont(font)
 
                # Draw the line number right justified at the y position of the
                # line. 3 is a magic padding number. drawText(x, y, text).
                painter.drawText(self.width() - font_metrics.width(str(line_count)) - 3, round(position.y()) - contents_y + font_metrics.ascent(), str(line_count))
 
                # Remove the bold style if it was set previously.
                if bold:
                    font = painter.font()
                    font.setBold(False)
                    painter.setFont(font)
 
                block = block.next()
 
            self.highest_line = line_count

            painter.end()
 
            QWidget.paintEvent(self, event)
 
 
    def __init__(self, *args):
        QFrame.__init__(self, *args)

        self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
 
        self.edit = self.TextBox()
        self.edit.setFrameStyle(QFrame.NoFrame)
        self.edit.setAcceptRichText(False)
        if sys.platform.startswith('win'):
            self.edit.setFontFamily('Courier')
        else:
            self.edit.setFontFamily('Monospace')
        self.edit.setTextColor(QtCore.Qt.white)
        pal = self.edit.palette()
        pal.setColor(QtGui.QPalette.Base, QColor(46, 52, 54))
        pal.setColor(QtGui.QPalette.Text, QColor(235, 237, 239))
        self.edit.setPalette(pal)
        highlighter(self.edit)
 
        self.number_bar = self.NumberBar()
        self.number_bar.setTextEdit(self.edit)

        hbox = QHBoxLayout(self)
        hbox.setSpacing(0)
        hbox.setMargin(0)
        hbox.addWidget(self.number_bar)
        hbox.addWidget(self.edit)
 
        self.edit.installEventFilter(self)
        self.edit.viewport().installEventFilter(self)

    def eventFilter(self, object, event):
        # Update the line numbers for all events on the text edit and the viewport.
        # This is easier than connecting all necessary signals.
        if object in (self.edit, self.edit.viewport()):
            self.number_bar.update()
            return False
        return QFrame.eventFilter(object, event)

    def getTextEdit(self):
        return self.edit

    def load_file(self, filename=''):
        if os.path.isfile(filename):
            self.orig     = str(filename)
            self.path     = os.path.normpath(str(filename))
            self.dirname  = os.path.dirname(self.path)
            self.filename = os.path.basename(self.path)
            self.edit.setDocumentPath(self.path)
            self.edit.setText(open(filename, 'r').read())
            
